﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Text;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.WowAddonStudio.FrameXml.Formats.Mpq;

namespace Microsoft.WowAddonStudio.FrameXml.Services
{
	/// <summary>
	/// Contains a <see cref="PrivateFontCollection"/>, holding fonts loaded through a <see cref="ResourceFileLoader"/>.
	/// </summary>
	[Guid("8BAFCFD6-33EA-4C24-A674-96FABCC12C99")]
	public class FrameXmlFontService : FrameXmlResourceService
	{
		private Dictionary<string, PrivateFontCollection> fontCollections = new Dictionary<string, PrivateFontCollection>();

		/// <summary>
		/// Initializes a new instance of the <see cref="FrameXmlFontService"/> class.
		/// </summary>
		/// <param name="resourceFileLoader">The resource file loader.</param>
		public FrameXmlFontService(ResourceFileLoader resourceFileLoader)
			: base(resourceFileLoader)
		{
		}

		/// <summary>
		/// Checks whether a font exists.
		/// </summary>
		/// <param name="fontPath">The path of the font.</param>
		/// <returns>True, if the font exists; False otherwise;</returns>
		public virtual bool FontExists(string fontPath)
		{
			if (fontPath == null)
			    return false;

			// Check whether already cached or resource file exists
			return fontCollections.ContainsKey(fontPath) || ResourceFileLoader.ResourceFileExists(fontPath);
		}

		/// <summary>
		/// Gets a font resource.
		/// </summary>
		/// <param name="fontPath">The path of the font.</param>
		/// <returns>An instance of the <see cref="FontFamily"/> class, if the font was found; <c>null</c> otherwise.</returns>
		public virtual FontFamily GetFont(string fontPath)
		{
			if (disposed)
				throw new ObjectDisposedException("MpqFontCollection");
			if (fontPath == null)
				throw new ArgumentNullException("fontPath");

			if (!fontCollections.ContainsKey(fontPath))
			{
				PrivateFontCollection addedFontCollection = AddMemoryFont(fontPath);
				if (addedFontCollection != null)
				{
					fontCollections.Add(fontPath, addedFontCollection);
					return addedFontCollection.Families[0];
				}
			}
			else
				return fontCollections[fontPath].Families[0];

			return null;
		}

		/// <summary>
		/// Adds the memory font.
		/// </summary>
		/// <param name="fontPath">The font path.</param>
		/// <returns></returns>
		protected virtual PrivateFontCollection AddMemoryFont(string fontPath)
		{
			try
			{
				var privateFontCollection = new PrivateFontCollection();

				using (Stream fontStream = ResourceFileLoader.GetResourceFileStream(fontPath))
				{
					if (fontStream == null)
						return null;

					IntPtr fontBuffer;
					int bytesRead;

					if (fontStream is MpqStream)
					{
						var mpqFontStream = (MpqStream) fontStream;
						bytesRead = mpqFontStream.ReadCoTaskMem((int) fontStream.Length, out fontBuffer);
					}
					else
						throw new NotImplementedException();

					try
					{
						privateFontCollection.AddMemoryFont(fontBuffer, bytesRead);
					}
					finally
					{
						Marshal.FreeCoTaskMem(fontBuffer);
					}
				}

				// Return the last FontFamily added
				return privateFontCollection;
			}
			catch (StormLibException)
			{
				return null;
			}
		}

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		protected override void Dispose(bool disposing)
		{
			if (!disposed && disposing)
			{
				try
				{
					if (fontCollections != null)
					{
						foreach (var privateFontCollection in fontCollections.Values)
							privateFontCollection.Dispose();

						fontCollections = null;
					}
					disposed = true;
				}
				finally
				{
					base.Dispose();
				}
			}
		}
	}
}